package org.blame.lotterysystem.service.impl;

import org.blame.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import org.blame.lotterysystem.common.exception.ServiceException;
import org.blame.lotterysystem.common.utils.JacksonUtil;
import org.blame.lotterysystem.common.utils.RedisUtil;
import org.blame.lotterysystem.controller.param.DrawPrizeParam;
import org.blame.lotterysystem.controller.param.ShowWinningRecordsParam;
import org.blame.lotterysystem.dao.dataObject.*;
import org.blame.lotterysystem.dao.mapper.*;
import org.blame.lotterysystem.service.DrawPrizeService;

import org.blame.lotterysystem.service.dto.ShowWinningRecordsListTDTO;
import org.blame.lotterysystem.service.enums.ActivityPrizeEnum;
import org.blame.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import org.blame.lotterysystem.service.enums.ActivityStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static org.blame.lotterysystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static org.blame.lotterysystem.common.config.DirectRabbitConfig.ROUTING;


@Service
public class DrawPrizeServiceImpl implements DrawPrizeService {

    private static final Logger logger= LoggerFactory.getLogger(DrawPrizeServiceImpl.class);

    private final String WINNING_RECORDS_PREFIX = "WINNING_RECORDS_";
    private final Long WINNING_RECORDS_TIMEOUT = 60 * 60 * 24 * 2L;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private WinningRecordMapper winningRecordMapper;

    @Autowired
    private RedisUtil redisUtil;
    @Override
    public void drawPrize(DrawPrizeParam param) {
        logger.info("drawPrize  DrawPrizeParam:{}",param);

        String messageId=String.valueOf(UUID.randomUUID());
        String messageDate= JacksonUtil.writeValueAsString(param);
        String creatTime= String.valueOf(new Date());
        Map<String,String> map=new HashMap<>();
        map.put("messageId",messageId);
        map.put("messageDate",messageDate);
        map.put("creatTime",creatTime);
        rabbitTemplate.convertAndSend(EXCHANGE_NAME,ROUTING,map);
    }

    @Override
    public boolean checkDrawPrizeParam(DrawPrizeParam param) {

        ActivityDO activityDO=activityMapper.selectById(param.getActivityId());

        ActivityPrizeDO activityPrizeDO=activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());
        // 活动或奖品是否存在
        if(activityDO==null || activityPrizeDO==null){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY);
        }

        // 活动是否有效
        if(activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_COMPLETED);
        }

        // 奖品是否有效
        if(activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeEnum.COMPLETED.name())){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED);
        }

        // 中奖者人数是否和设置奖品数量一致
        if(activityPrizeDO.getPrizeAmount()!=param.getWinnerList().size()){
            throw new ServiceException(ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR);
        }

        return false;
    }

    @Override
    public List<WinningRecordDO> saveWinnerRecords(DrawPrizeParam param) {

        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        List<UserDO> userDOList=userMapper.batchSelectByIds(
                param.getWinnerList()
                .stream()
                .map(DrawPrizeParam.Winner::getUserId)
                .collect(Collectors.toList()));
        PrizeDO prizeDO = prizeMapper.selectById(param.getPrizeId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());
        List<WinningRecordDO> winningRecordDOS=userDOList
                .stream()
                .map(userDO -> {
                    WinningRecordDO winningRecordDO=new WinningRecordDO();
                    winningRecordDO.setActivityId(activityDO.getId());
                    winningRecordDO.setActivityName(activityDO.getActivityName());
                    winningRecordDO.setPrizeId(prizeDO.getId());
                    winningRecordDO.setPrizeName(prizeDO.getName());
                    winningRecordDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
                    winningRecordDO.setWinnerId(userDO.getId());
                    winningRecordDO.setWinnerName(userDO.getUserName());
                    winningRecordDO.setWinnerEmail(userDO.getEmail());
                    winningRecordDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
                    winningRecordDO.setWinningTime(param.getWinningTime());
                    return winningRecordDO;
                }).collect(Collectors.toList());
        winningRecordMapper.batchInsert(winningRecordDOS);

        // 缓存中奖者记录
        // 1、缓存奖品维度中奖记录(WinningRecord_activityId_prizeId, winningRecordDOList（奖品维度的中奖名单）)
        cacheWinningRecords(param.getActivityId() + "_" + param.getPrizeId(),
                winningRecordDOS,
                WINNING_RECORDS_TIMEOUT);

        // 2、缓存活动维度中奖记录(WinningRecord_activityId, winningRecordDOList(活动维度的中奖名单))
        // 当活动已完成再去存放活动维度中奖记录
        if (activityDO.getStatus()
                .equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
            // 查询活动维度的全量中奖记录
            List<WinningRecordDO> allList = winningRecordMapper.selectByActivityId(param.getActivityId());
            cacheWinningRecords(String.valueOf(param.getActivityId()),
                    allList,
                    WINNING_RECORDS_TIMEOUT);
        }

        return winningRecordDOS;

    }

    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        if(activityId==null){
            logger.warn("删除的活动记录为空");
        }

//        删除数据表
        winningRecordMapper.deleteRecords(activityId,prizeId);
//        删除缓存
        if(prizeId!=null){
            deleteWinningRecords(activityId + "_" + prizeId);
        }

        deleteWinningRecords(String.valueOf(activityId));

    }

    @Override
    public List<ShowWinningRecordsListTDTO> showRecords(ShowWinningRecordsParam param) {
        String key=null==param.getPrizeId()
                ?String.valueOf(param.getActivityId())
                :param.getActivityId()+"_"+param.getPrizeId();
//        查询缓存
        List<WinningRecordDO> winningRecords = getWinningRecords(key);
        if(!CollectionUtils.isEmpty(winningRecords)){
            return convertToShowWinningRecordsListTDTO(winningRecords);
        }
//        如果在缓存中不存在，查询数据库
        winningRecords=winningRecordMapper.selectByActivityIdOrPrizeId(param.getActivityId(),param.getPrizeId());
        if(CollectionUtils.isEmpty(winningRecords)){
            return Arrays.asList();
        }
        //       将数据库中查询的内容存入到redis中
        cacheWinningRecords(key,winningRecords,WINNING_RECORDS_TIMEOUT);

        return convertToShowWinningRecordsListTDTO(winningRecords);


    }

    private List<ShowWinningRecordsListTDTO> convertToShowWinningRecordsListTDTO(List<WinningRecordDO> winningRecords) {
        if (CollectionUtils.isEmpty(winningRecords)){
            logger.error("winningRecords 不存在:{}",JacksonUtil.writeValueAsString(winningRecords));
            return Arrays.asList();
        }

        return winningRecords.stream()
                .map(winningRecordDO -> {
                    ShowWinningRecordsListTDTO showWinningRecordsListTDTO=new ShowWinningRecordsListTDTO();
                    showWinningRecordsListTDTO.setWinningTime(winningRecordDO.getWinningTime());
                    showWinningRecordsListTDTO.setPrizeTier(ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier()));
                    showWinningRecordsListTDTO.setPrizeName(winningRecordDO.getPrizeName());
                    showWinningRecordsListTDTO.setWinnerId(winningRecordDO.getWinnerId());
                    showWinningRecordsListTDTO.setWinnerName(winningRecordDO.getWinnerName());
                    return showWinningRecordsListTDTO;
                }).collect(Collectors.toList());

    }

    private void deleteWinningRecords(String key) {
        try {
            if(redisUtil.hasKey(WINNING_RECORDS_PREFIX+key)){
                redisUtil.deleteKey(WINNING_RECORDS_PREFIX+key);
            }
        }catch (Exception e){
            logger.error("从缓存中删除失败,key:{}", key);
        }
    }

    private void cacheWinningRecords(String s, List<WinningRecordDO> winningRecordDOS, Long time) {
        String str = "";

        try {
            if (!StringUtils.hasText(s)
                    || CollectionUtils.isEmpty(winningRecordDOS)) {
                logger.warn("要缓存的内容为空！key:{}, value:{}", s, JacksonUtil.writeValueAsString(winningRecordDOS));
                return;
            }
            str = JacksonUtil.writeValueAsString(winningRecordDOS);
            redisUtil.set(WINNING_RECORDS_PREFIX + s,
                    str,
                    time);

        } catch (Exception e) {
            logger.error("缓存中奖记录异常！key:{}, value:{}", WINNING_RECORDS_PREFIX + s, str);
        }

    }

    private List<WinningRecordDO> getWinningRecords(String key) {
        try {
            if (!StringUtils.hasText(key)) {
                logger.warn("要从缓存中查询中奖记录的key为空！");
                return Arrays.asList();
            }
            String str = redisUtil.get(WINNING_RECORDS_PREFIX + key);
            if (!StringUtils.hasText(str)) {
                return Arrays.asList();
            }

            return JacksonUtil.readListValue(str, WinningRecordDO.class);
        } catch (Exception e) {
            logger.error("从缓存中查询中奖记录异常！key:{}", WINNING_RECORDS_PREFIX + key);
            return Arrays.asList();
        }
    }
    }